package com.bioscript.standalone.IO;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import com.bioscript.standalone.IO.GbkParsingEvents.GBKEventTypes;
import com.bioscript.standalone.IO.baseClasses.BaseEvent.BaseEventTypes;
import com.bioscript.standalone.dataStructures.Range;

public class GbkFileReader extends GbkFileParser<ArrayList<String>>{

	private HashMap<String,ArrayList<String>> geneMap = 
		new HashMap<String, ArrayList<String>>();
	private HashMap<String,ArrayList<String>> cdsMap = 
		new HashMap<String, ArrayList<String>>();
	private HashMap<String,ArrayList<String>> trna = 
		new HashMap<String, ArrayList<String>>();
	private HashMap<String,ArrayList<String>> ncrna = 
		new HashMap<String, ArrayList<String>>();
	private HashMap<String,ArrayList<String>> rRNA = 
		new HashMap<String, ArrayList<String>>();
	private HashMap<String,ArrayList<String>> tmRNA = 
		new HashMap<String, ArrayList<String>>();

	private String gbkFile = null;
	private String referenceSeqFile = null;

	private boolean collectGenes = false;
	private boolean collectCds = false;
	public boolean isReadTranslation() {
		return readTranslation;
	}

	public GbkFileReader setReadTranslation(boolean readTranslation) {
		this.readTranslation = readTranslation;
		return this;
	}

	private boolean collectNcRNAs = false;
	private boolean colecttRNAs = false;
	private boolean collecttmRNAs = false;
	private boolean collectrRNAs = false;
	private boolean readReference = false;
	private boolean readTranslation = false;

	private FileWriter refSeqWriter = null;
	private String lastGene = null;
//	private String genomeName = null;

	public void run() throws IOException
	{
		parse(new File(gbkFile));
	}

	public String getGbkFile() {
		return gbkFile;
	}

	public GbkFileReader setGbkFile(String gbkFile) {
		this.gbkFile = gbkFile;
		return this;
	}

	public HashMap<String, ArrayList<String>> getGeneMap() {
		return geneMap;
	}

	public List<Range> toRange(HashMap<String, ArrayList<String>> map)
	{
		List<Range> ranges = new ArrayList<Range>();
		Set<String> keySet = map.keySet();
		for(String key : keySet)
		{
			ArrayList<String> lst = map.get(key);
			for(String entry : lst)
			{
				String pos = entry.split("\t")[0];
				int lowerLimit = Integer.parseInt(pos.split("\\.\\.")[0]);
				int upperLimit = Integer.parseInt(pos.split("\\.\\.")[1]);
				Range range = new Range(lowerLimit, upperLimit, key );
				range.setMetadata(entry);
				if (entry.endsWith("-")) range.setReverse();
				ranges.add(range);
			}
		}
		return ranges; 
	}

	public HashMap<String, ArrayList<String>> getCdsMap() {
		return cdsMap;
	}

	public HashMap<String, ArrayList<String>> getTrna() {
		return trna;
	}

	public HashMap<String, ArrayList<String>> getNcrna() {
		return ncrna;
	}

	public HashMap<String, ArrayList<String>> getrRNA() {
		return rRNA;
	}

	public HashMap<String, ArrayList<String>> getTmRNA() {
		return tmRNA;
	}

	public void setLastGene(String lastGene) {
		this.lastGene = lastGene;
	}

	@Override
	public void handleEvent(GbkParsingEvents event) {
		try
		{
			if (event.getEventType() instanceof BaseEventTypes)
			{
				switch((BaseEventTypes)event.getEventType())
				{
				case docStarted : break;
				case docEnded: 
					if (refSeqWriter != null)
					{
						refSeqWriter.flush();
						refSeqWriter.close();
						refSeqWriter = null;
					}
					break;
				}
			}
			else if (event.getEventType() instanceof GBKEventTypes)
			{
				switch((GBKEventTypes)event.getEventType())
				{

				case accessionNumber:
					if (readReference)
					{
//						String str = (String)event.getSource();
//						genomeName = str.trim().replaceAll("( )+", " ").split(" ")[1].trim();
					}
					break;
				case sequence:
				{
					String str = (String)event.getSource();
					String[] params = str.trim().replaceAll("( )+", " ").split(" ");
					StringBuffer strBuffer = new StringBuffer();
					for(int i = 1; i < params.length;i++)
					{
						strBuffer.append(params[i]);
					}
					//					refSeqWriter.write(strBuffer.toString().trim()+"\n");
					refSeqWriter.write(strBuffer.toString().trim());
					break;
				}
				case sequenceStart: 
				{
					if (!readReference) setStopReading(true);
					else
					{
						refSeqWriter = new FileWriter(referenceSeqFile);
						//						refSeqWriter.write(">" + genomeName + "\n");
					}
					break;
				}
				case rRNA:
				{
					if (colecttRNAs)
					{
						String cdsName = getFeatureName();
						String positionInfo = getPositionalInfo();
						// This is to check that we are not reading a gene info with no position info
						assert(positionInfo != null);
						String orientation = "+";
						if (positionInfo.contains("complement"))
						{
							orientation = "-";
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.lastIndexOf(")"));
						}
						if (positionInfo.contains("join"))
						{
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.indexOf(")")); 
							String[] subParts = positionInfo.split(",");
							String start = subParts[0].split("\\.\\.")[0];
							String end = subParts[subParts.length - 1].split("\\.\\.")[1];
							positionInfo = start+".."+end;
						}
						positionInfo = positionInfo + "\t" + orientation;
						ArrayList<String> list = null;
						// if we have already read this gene before
						if (rRNA.containsKey(cdsName))
						{
							// fetch the positions list corresponding to this geneName
							list = rRNA.get(cdsName);
						}
						else
						{
							list = new ArrayList<String>();
						}
						list.add(positionInfo);
						rRNA.put(cdsName, list);
					}
					break;
				}
				case tRNA:
				{
					if (collectrRNAs)
					{
						String cdsName = getFeatureName();
						String positionInfo = getPositionalInfo();
						// This is to check that we are not reading a gene info with no position info
						assert(positionInfo != null);
						String orientation = "+";
						if (positionInfo.contains("complement"))
						{
							orientation = "-";
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.lastIndexOf(")"));
						}
						if (positionInfo.contains("join"))
						{
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.indexOf(")")); 
							String[] subParts = positionInfo.split(",");
							String start = subParts[0].split("\\.\\.")[0];
							String end = subParts[subParts.length - 1].split("\\.\\.")[1];
							positionInfo = start+".."+end;
						}
						positionInfo = positionInfo + "\t" + orientation;
						ArrayList<String> list = null;
						// if we have already read this gene before
						if (trna.containsKey(cdsName))
						{
							// fetch the positions list corresponding to this geneName
							list = trna.get(cdsName);
						}
						else
						{
							list = new ArrayList<String>();
						}
						list.add(positionInfo);
						trna.put(cdsName, list);
					}
					break;
				}
				case cds: 
				{
					if (collectCds)
					{
						String cdsName = getFeatureName();
						String positionInfo = getPositionalInfo();
						// This is to check that we are not reading a gene info with no position info
						assert(positionInfo != null);
						String orientation = "+";
						if (positionInfo.contains("complement"))
						{
							orientation = "-";
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.lastIndexOf(")"));
						}
						if (positionInfo.contains("join"))
						{
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.indexOf(")")); 
							String[] subParts = positionInfo.split(",");
							String start = subParts[0].split("\\.\\.")[0];
							String end = subParts[subParts.length - 1].split("\\.\\.")[1];
							positionInfo = start+".."+end;
						}
						positionInfo = positionInfo + "\t" + orientation;
						ArrayList<String> list = null;
						// if we have already read this gene before
						if (cdsMap.containsKey(cdsName))
						{
							// fetch the positions list corresponding to this geneName
							list = cdsMap.get(cdsName);
						}
						else
						{
							list = new ArrayList<String>();
						}
						list.add(positionInfo);
						if (readTranslation)
						{
							list.add(getTranslation());
						}
						cdsMap.put(cdsName, list);
					}
					break;
				}
				case tmRNA:
				{
					if (collecttmRNAs)
					{
						String positionInfo = getPositionalInfo();
						// This is to check that we are not reading a gene info with no position info
						assert(positionInfo != null);
						String orientation = "+";
						if (positionInfo.contains("complement"))
						{
							orientation = "-";
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.lastIndexOf(")"));
						}
						if (positionInfo.contains("join"))
						{
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.indexOf(")")); 
							String[] subParts = positionInfo.split(",");
							String start = subParts[0].split("\\.\\.")[0];
							String end = subParts[subParts.length - 1].split("\\.\\.")[1];
							positionInfo = start+".."+end;
						}
						positionInfo = positionInfo + "\t" + orientation;
						ArrayList<String> list = null;
						// if we have already read this gene before
						if (tmRNA.containsKey(lastGene))
						{
							// fetch the positions list corresponding to this geneName
							list = tmRNA.get(lastGene);
						}
						else
						{
							list = new ArrayList<String>();
						}
						list.add(positionInfo);
						tmRNA.put(lastGene, list);
					}
					break;
				}
				case ncRNA:
				{
					if (collectNcRNAs)
					{
						String positionInfo = getPositionalInfo();
						// This is to check that we are not reading a gene info with no position info
						assert(positionInfo != null);
						String orientation = "+";
						if (positionInfo.contains("complement"))
						{
							orientation = "-";
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.lastIndexOf(")"));
						}
						if (positionInfo.contains("join"))
						{
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.indexOf(")")); 
							String[] subParts = positionInfo.split(",");
							String start = subParts[0].split("\\.\\.")[0];
							String end = subParts[subParts.length - 1].split("\\.\\.")[1];
							positionInfo = start+".."+end;
						}
						positionInfo = positionInfo + "\t" + orientation;
						ArrayList<String> list = null;
						// if we have already read this gene before
						if (ncrna.containsKey(lastGene))
						{
							// fetch the positions list corresponding to this geneName
							list = ncrna.get(lastGene);
						}
						else
						{
							list = new ArrayList<String>();
						}
						list.add(positionInfo);
						ncrna.put(lastGene, list);
					}
					break;
				}

				case gene:
				{
					if (collectGenes)
					{
						String geneName = getFeatureName();
						String positionInfo = getPositionalInfo();
						// This is to check that we are not reading a gene info with no position info
						assert(positionInfo != null);
						String orientation = "+";
						if (positionInfo.contains("complement"))
						{
							orientation = "-";
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.lastIndexOf(")"));
						}
						if (positionInfo.contains("join"))
						{
							positionInfo = positionInfo.substring(positionInfo.indexOf("(") + 1,positionInfo.indexOf(")")); 
							String[] subParts = positionInfo.split(",");
							String start = subParts[0].split("\\.\\.")[0];
							String end = subParts[subParts.length - 1].split("\\.\\.")[1];
							positionInfo = start+".."+end;
						}
						positionInfo = positionInfo + "\t" + orientation;
						ArrayList<String> list = null;
						// if we have already read this gene before
						if (geneMap.containsKey(geneName))
						{
							// fetch the positions list corresponding to this geneName
							list = geneMap.get(geneName);
						}
						else
						{
							list = new ArrayList<String>();
						}
						list.add(positionInfo);
						geneMap.put(geneName, list);
						lastGene = geneName;
					}
					break;
				}
				}
			}	
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.err.println();
			System.exit(-1);
		}
	}

	public GbkFileReader setCollectGenes(boolean collectGenes) {
		this.collectGenes = collectGenes;
		return this;
	}

	public GbkFileReader setCollectCds(boolean collectCds) {
		this.collectCds = collectCds;
		return this;
	}

	public GbkFileReader setCollectNcRNAs(boolean collectNcRNAs) {
		this.collectNcRNAs = collectNcRNAs;
		return this;
	}

	public GbkFileReader setColecttRNAs(boolean colecttRNAs) {
		this.colecttRNAs = colecttRNAs;
		return this;
	}

	public GbkFileReader setCollecttmRNAs(boolean collecttmRNAs) {
		this.collecttmRNAs = collecttmRNAs;
		return this;
	}

	public GbkFileReader setCollectrRNAs(boolean collectrRNAs) {
		this.collectrRNAs = collectrRNAs;
		return this;
	}

	public GbkFileReader setReadReference(boolean readReference) {
		this.readReference = readReference;
		return this;
	}

	public GbkFileReader setReferenceSeqFile(String referenceSeqFile) {
		this.referenceSeqFile = referenceSeqFile;
		return this;
	}
}
